package view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.border.BevelBorder;
import javax.swing.border.SoftBevelBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

import modelInterfaces.IResult;
import modelInterfaces.ITest;
import utils.NoEditableTableModel;
import viewInterfaces.IViewBenchmarkPanel;
import controllerInterfaces.IControllerBenchmarkPanel;

/**
 * View used to perform all tasks of benchmark.
 * 
 * @author Andy Cullache
 */
public class ViewBenchmarkPanel extends JPanel implements IViewBenchmarkPanel{

	private IControllerBenchmarkPanel myController;
	private List<ITest> tests;
	private JList<ITest> list;
	private DefaultTableModel dtm;
	private JTable table;
	private JButton btnRun;
	
	/**
	 * Default constructor. Create the panel.
	 */
	public ViewBenchmarkPanel() {
		addComponentListener(new ComponentAdapter() {
			@Override
			public void componentShown(ComponentEvent arg0) {
				//setTestSetup();
				//setTable();
			}
		});
		JPanel panel = new JPanel();
		panel.setMaximumSize(new Dimension(100, 100));
		panel.setAutoscrolls(true);
		panel.setBorder(new TitledBorder(null, "Test List", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		
		JPanel panel_1 = new JPanel();
		panel_1.setBorder(new TitledBorder(null, "Result Test", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		GroupLayout groupLayout = new GroupLayout(this);
		groupLayout.setHorizontalGroup(
			groupLayout.createParallelGroup(Alignment.TRAILING)
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addGroup(groupLayout.createParallelGroup(Alignment.TRAILING)
						.addComponent(panel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 507, Short.MAX_VALUE)
						.addComponent(panel_1, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 507, Short.MAX_VALUE))
					.addContainerGap())
		);
		groupLayout.setVerticalGroup(
			groupLayout.createParallelGroup(Alignment.TRAILING)
				.addGroup(groupLayout.createSequentialGroup()
					.addContainerGap()
					.addComponent(panel, GroupLayout.DEFAULT_SIZE, 228, Short.MAX_VALUE)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(panel_1, GroupLayout.DEFAULT_SIZE, 202, Short.MAX_VALUE)
					.addContainerGap())
		);
		panel_1.setLayout(new BorderLayout(0, 0));
		
		JPanel panel_2 = new JPanel();
		panel_1.add(panel_2, BorderLayout.WEST);
		
		JButton btnNewButton = new JButton("Save Test Result");
		btnNewButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				myController.ChooseDirectoryToSaveTest();
			}
		});
		
		JButton btnNewButton_1 = new JButton("Load Test Result");
		btnNewButton_1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				myController.ChooseDirectoryToLoadTest();
			}
		});
		
		JButton btnNewButton_2 = new JButton("Clean Test Result");
		btnNewButton_2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				myController.clearResultTest();
				setTable();
			}
		});
		GroupLayout gl_panel_2 = new GroupLayout(panel_2);
		gl_panel_2.setHorizontalGroup(
			gl_panel_2.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_2.createSequentialGroup()
					.addContainerGap()
					.addGroup(gl_panel_2.createParallelGroup(Alignment.LEADING, false)
						.addComponent(btnNewButton_2, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
						.addComponent(btnNewButton_1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
						.addComponent(btnNewButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
					.addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
		);
		gl_panel_2.setVerticalGroup(
			gl_panel_2.createParallelGroup(Alignment.LEADING)
				.addGroup(gl_panel_2.createSequentialGroup()
					.addContainerGap()
					.addComponent(btnNewButton)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(btnNewButton_1)
					.addPreferredGap(ComponentPlacement.RELATED)
					.addComponent(btnNewButton_2)
					.addContainerGap(82, Short.MAX_VALUE))
		);
		panel_2.setLayout(gl_panel_2);
		
		JPanel panel_4 = new JPanel();
		panel_1.add(panel_4, BorderLayout.SOUTH);
		panel_4.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
		
		JButton btnNewButton_3 = new JButton("See Graph");
		btnNewButton_3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				myController.showDialogTestGraphic();
			}
		});
		panel_4.add(btnNewButton_3);
		
		JButton btnNewButton_4 = new JButton("Generate\r\n Output");
		btnNewButton_4.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				myController.showOutputSetupView();
			}
		});
		panel_4.add(btnNewButton_4);
		
		JScrollPane scrollPane = new JScrollPane();
		panel_1.add(scrollPane, BorderLayout.CENTER);
		
		table = new JTable();
		table.setUpdateSelectionOnSort(false);
		table.setBorder(new SoftBevelBorder(BevelBorder.LOWERED, null, null, null, null));
		scrollPane.setViewportView(table);
		panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
		
		JScrollPane scrollPane_1 = new JScrollPane();
		
		list = new JList<ITest>();
		scrollPane_1.setViewportView(list);
		list.setBorder(new SoftBevelBorder(BevelBorder.LOWERED, null, null, null, null));
		list.setVisibleRowCount(10);
		panel.add(scrollPane_1);
		
		btnRun = new JButton("Run");
		btnRun.setEnabled(false);
		btnRun.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				if(!list.isSelectionEmpty()){
					checkSelectedTest();
					myController.showDialogRunTest();
					setTable();
				}
			}
		});
		
		list.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent arg0) {
				if(list.isSelectionEmpty())
				{
					btnRun.setEnabled(false);
				}
				else
				{
					btnRun.setEnabled(true);
				}
			}
		});
		
		scrollPane_1.setRowHeaderView(btnRun);
		setLayout(groupLayout);

	}
	
	/**
	 * Get list of selected test to run.
	 * 
	 * @return      Set of selected test.
	 */
	public List<ITest> getSelectedTest() {
		return list.getSelectedValuesList();
	}
	
	/**
	 * Checks whether the test are available for the execution, containing algorithms
	 *  and metrics, otherwise shows an error message.
	 */
	public void checkSelectedTest(){
		String aux = "";
		for(ITest test : list.getSelectedValuesList()){
			if(test.getAlgorithms().size()==0 || test.getMetrics().size()==0){
				aux = aux + test.getName() + "," ;
			}
		}
		if(aux!="")
			renderError(30, "The test: "+ aux + " is(are) incomplete");
	}

	/**
	 * Sets the controller of this view.
	 * 
	 * @param myController      Benchmark panel controller.
	 */
	public void setController(IControllerBenchmarkPanel myController){
		this.myController = myController;
	}
	
	/**
	 * Mark with a string "--Error" to the test to be defective, do not have metrics and algorithms associated.
	 */
	public void setTestSetup(){
		this.tests = this.myController.getTests();
		DefaultListModel<ITest> models = new DefaultListModel<ITest>();
		if(this.tests != null){
			for(ITest test: tests){
				if(test.getName().endsWith("--Error")){
					String aux = test.getName().replaceAll("--Error", "");
					test.setName(aux);
				}
				if(test.getAlgorithms().size()==0 || test.getMetrics().size()==0)
					test.setName(test.getName()+"--Error");
				models.addElement(test);
			}
			list.setModel(models);
		}
	}

	/**
	 * Sets the columns of the table of test results.
	 */
    public void setTable(){
    	dtm = new NoEditableTableModel();
    	dtm.addColumn("Test Name");
    	dtm.addColumn("Algorithm Name");
    	
    	Set<String> columns = getColumnsMetric();
    	if(columns != null){
	    	for(String column : columns){
	            dtm.addColumn(column);
	    	}
    	}
        addListTestToTable(this.myController.getTests());
        addListTestToTable(this.myController.getLoadTest());
        this.table.setModel(dtm);
    }
    
    /**
     * Adds a list of test results delivered.
     * 
     * @param tests      Test list containing results.
     */
    private void addListTestToTable(List<ITest> tests){
    	if(tests != null){
	        Object[] objetoFila = null;
	        int i;
	    	for(ITest test : tests){
	        	Map<String, List<IResult>> resultMap = test.getResultMap();
	        	if(resultMap.size()!=0){
		        	for(String alg : resultMap.keySet()){
		        		objetoFila= new Object[dtm.getColumnCount()];
		            	i = 0;
		            	objetoFila[i] = test.getName();
		            	i++;
		            	objetoFila[i] = alg;
		            	i++;	        	
		        	for(int j = i; j<dtm.getColumnCount(); j++){
		        		int size = 0;
		        		objetoFila[j] = 0.0;
		        		for(IResult res : resultMap.get(alg)){
		                	if(dtm.getColumnName(j).equals(res.getMetric().getName())){
		                		objetoFila[j] = (double)objetoFila[j] + res.getResultAverage();
		                		size++;                	
		                	}
		            	}
		        		if(size==0)
		        			objetoFila[j] = null;
		        		else
		        			objetoFila[j] = (double) ((double)objetoFila[j]/size);
		        	}
		        	dtm.addRow(objetoFila);
		        	}
	        	}
	    	}
    	}
    }

    /**
     * Gets a list with the names of all the different metrics employed in the test run 
     * and then display them in the results table.
     * 
     * @return      List of metric names.
     */
	private Set<String> getColumnsMetric() {
		Set<String> columns = new HashSet<String>();
		List<ITest> tests = this.myController.getTests();
		if(tests != null){
			for(ITest test : tests){
				if(!test.getResultMap().isEmpty()){
					for(IResult resM : test.getResultMap().values().iterator().next()){
						columns.add(resM.getMetric().getName());
					}
				}
			}
			tests = myController.getLoadTest();
			for(ITest test : tests){
				for(IResult resM : test.getResultMap().values().iterator().next()){
					columns.add(resM.getMetric().getName());
				}
			}
		}
		return columns;	
	}

	@Override
	public JPanel GetPanel() {
		return this;
	}
	
	/**
	 * Method used to display error messages with the respective code.
	 * 
	 * @param code      Error code.
	 * @param message      Message code.
	 */
	private void renderError(int code, String message) {
		javax.swing.JOptionPane.showMessageDialog(this, code + " - " + message, "Error",javax.swing.JOptionPane.ERROR_MESSAGE);	
	}
}
